﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CamShared;
using System.IO;
using System.Drawing;
using System.Net.Sockets;
using System.Threading;
using System.Diagnostics;
using UPnP;


namespace MotionJpeg
{
    public class MotionJpeg : ICameraConnector
    {

        List<MJpegCamera> cams = new List<MJpegCamera>();

        public MotionJpeg()
        {

        }

        void SharedHost_OnDeviceDiscovered(Device d)
        {
            if (d.USN.Contains("MediaServer-1_0-00000e3b06c9e3"))
            {


                MJpegCamera m = new MJpegCamera();
                m.Port = 4321;
                m.Name = "Hawking HNC230G";

                string ip = d.Location.Remove(d.Location.IndexOf(':', 6));
                ip = ip.Substring(7);
                m.HostName = ip;


                foreach (MJpegCamera c in cams.ToArray())
                {
                    if (c.HostName == m.HostName)
                    {
                        Trace.WriteLine("Not Re-Adding Camera " + m.Name + " at " + m.HostName);
                        return;
                    }
                }

                cams.Add(m);
                CameraDiscovered(m, this);
            }
        }


        public void DetectCameras()
        {
            //System.Threading.Thread.Sleep(1000 * Settings.Get("MJPEG Cameras|Detection Wait (seconds)", 30));

            
            MJpegCamera m = new MJpegCamera();
            m.Port = 4321;
            m.HostName = "192.168.1.200";
            m.Name = "HNC230G IP";
            CameraDiscovered(m, this);
            


            lock (UPnPDiscovery.SharedHost.DiscoveryLock)
            {
                foreach (Device d in UPnPDiscovery.SharedHost.DiscoveredDevices)
                {
                    SharedHost_OnDeviceDiscovered(d);
                }
                UPnPDiscovery.SharedHost.OnDeviceDiscovered += new UPnPDiscovery.DeviceDiscovery(SharedHost_OnDeviceDiscovered);

                if (!UPnPDiscovery.SharedHost.DiscoveryRequested)
                {
                    UPnPDiscovery.SharedHost.Discover(new SSDP());
                }
            }

        }

        public ICamera CreateCamera(string Name, string Class, string[] args)
        {
            throw new NotImplementedException();
        }

        public string Name
        {
            get { return "Motion-Jpeg (0110)"; }
        }

        public void Dispose()
        {

        }
        public event CamShared.CamShared.CameraDiscovered CameraDiscovered;

    }

    public class MJpegCamera : BaseCamera
    {
        TcpClient conn = new TcpClient();
        Thread workerThread = null;


        public override bool ShowConfig(IntPtr handle) { return false; }


        public string HostName { get; set; }
        public int Port { get; set; }
        public int ActiveUsers { get; set; }

        public MJpegCamera()
        {
            Name = "Generic MJPEG Camera";
            Class = "MJPEG Network Camera";
        }

        public bool Connected
        {
            get { return conn.Connected; }
        }

        public bool Connect()
        {
            try
            {
                if (conn != null)
                {
                    conn.Close();
                }
                conn = null;
                conn = new TcpClient();
                conn.Connect(HostName, Port);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }


        public static Bitmap BufferToImage(byte[] buffer)
        {
            if (buffer != null && buffer.Length > 0)
            {
                MemoryStream ms = new MemoryStream(buffer);
                return (Bitmap)Bitmap.FromStream(ms);
            }
            else
            {
                return null;
            }
        }

        public byte[] GetFrame()
        {
            if (conn == null || !conn.Connected)
            {
                Connect();
                if (!conn.Connected)
                {
                    //ErrorArrived(this, "Unable to connect to IP Camera", false);
                    Thread.Sleep(1000);
                    Trace.WriteLine("Error: not connected to camera");
                    return null;
                }
            }
            try
            {
                StreamWriter sw = new StreamWriter(conn.GetStream());

                sw.WriteLine("0110");
                sw.Flush();

                // get the high 8bits of the size
                int size = conn.GetStream().ReadByte() << 8;
                // get the low 8bits of the size
                size += conn.GetStream().ReadByte();

                // active users
                int j1 = conn.GetStream().ReadByte();
                ActiveUsers = j1;
                // unknown byte
                int j2 = conn.GetStream().ReadByte();

                if (size == 0)
                {
                    return null;
                }

                // buffer for JFIF data
                byte[] buffer = new byte[size];

                int r = 0;
                int fail = 0;
                while (r < size || fail > 5000)
                {
                    int rx = conn.GetStream().Read(buffer, r, Math.Min(size - r, 2048));
                    if (rx == -1)
                    {
                        break;
                    }
                    else
                    {
                        r += rx;
                    }
                    fail++;
                }
                if (buffer == null)
                {
                    Trace.WriteLine("Writing Null Buffer");
                }
                if (buffer.Length == 0)
                {
                    // Trace.WriteLine("Writing Empty Buffer: " + size);
                }
                return buffer;
            }
            catch (IOException ex)
            {
                if (ex.InnerException as ThreadAbortException == null)
                {
                    Trace.WriteLine("Socket Frame Error: " + ex.Message);
                    try
                    {
                        conn.Close();
                        conn = null;
                    }
                    catch { }
                    Thread.Sleep(100);
                }
                return null;
            }

            catch (Exception ex)
            {
                Trace.WriteLine("Camera Frame Error: " + ex);
                try
                {
                    conn.Close();
                    conn = null;
                }
                catch { }
                Thread.Sleep(100); 
                return null;
            }
        }

        private void ThreadEntry()
        {
            while (true)
            {
                try
                {
                    Bitmap img = BufferToImage(GetFrame());
                    if (img != null)
                    {
                        FrameArrived(this, img);
                        //Thread.Sleep(250);
                    }
                }
                catch (ThreadAbortException)
                {
                    // we know.
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("MJPEG ThreadEntry error: " + ex);
                }
                Thread.Sleep(1);
            }
        }

        protected override void OnActivated()
        {
            workerThread = new Thread(ThreadEntry);
            workerThread.Start();
        }

        protected override void OnDeActivated()
        {
            workerThread.Abort();
        }
    }
}
